import Request from './request';
import type {
  RequestInterceptor,
  TransformRequestData,
  ResponseInterceptor,
} from './types';
import {
  requestInterceptorCatcher,
  responseInterceptorCatcher,
  errorStatusCodeCatcher,
  errorApiCodeCatcher,
} from './error-catchers';
import { isObject, cloneDeep } from 'lodash-es';
import { baseUrl, mockUrl } from '@/apis/config';
import { login } from './login';

/** 处理请求头 */
const getHeaders = async (options: RequestOptions) => {
  const headers: Record<string, string> = {};
  // 检查登陆状态，默认需要授权
  if (options.auth === false) {
    // 不需要授权，不带token
  } else if (options.auth === 'optional') {
    // 可选授权，有token带token
    const authStore = $store.user();
    if (authStore.getTokenAvailable()) {
      headers.token = authStore.getToken();
    }
  } else {
    // 检查缓存token是否可用，不可用刷新
    const authStore = $store.user();
    if (!authStore.getTokenAvailable()) await login();
    if (!authStore.getTokenAvailable()) return;
    headers.token = authStore.getToken();
  }
  // 合并自定义请求头
  Object.assign(headers, options.headers);
  return headers;
};

/** 请求拦截器 */
const requestInterceptor: RequestInterceptor = async (
  options: RequestOptions,
) => {
  const opt = { ...options };
  // 用于重试的参数，如果存在该项则无视任何处理，直接请求
  if (opt?.requestOptions) return opt.requestOptions;

  // const headers = await getHeaders(opt);
  // if (!headers) return;
  // Reflect.set(opt, 'headers', headers);

  // mock
  if (opt.mock && import.meta.env.DEV) {
    opt.baseUrl = mockUrl;
  }
  return opt;
};

/** 请求前数据处理 */
const transformRequestData: TransformRequestData = async (
  options: RequestOptions,
) => {
  const opt = cloneDeep(options);
  // 用于重试的参数，如果存在该项则无视任何处理，直接请求
  if (opt?.requestOptions) return opt.requestOptions;
  // GET请求 删除时间戳参数
  if (opt.method === 'GET') {
    if (isObject(opt.data)) {
      Reflect.deleteProperty(opt.data, '_t');
    }
  }
  // 拼接query
  if (isObject(opt.query)) {
    let query = '';
    let sign = '?';
    for (const key in opt.query) {
      const keyName = encodeURIComponent(key);
      const val = opt.query[key];
      if (Array.isArray(val)) {
        for (const v of val) {
          query += `${sign}${keyName}=${encodeURIComponent(v ?? '')}`;
          sign = '&';
        }
      } else if (val !== undefined) {
        query += `${sign}${keyName}=${encodeURIComponent(val)}`;
        sign = '&';
      }
    }
    Reflect.set(opt, 'url', `${opt.url}${query}`);
  }
  // GET请求加时间戳防止缓存
  if (opt.method === 'GET') {
    // 添加时间戳
    const time = new Date().getTime();
    const sign = opt.url.includes('?') ? '&' : '?';
    Reflect.set(opt, 'url', `${opt.url}${sign}_t=${time}`);
  }
  return opt;
};

/** 响应拦截器 */
const responseInterceptor: ResponseInterceptor = async (
  options: RequestOptions,
  result: Result<AnyResult>,
  response: UniNamespace.RequestSuccessCallbackResult,
) => {
  // 捕获请求错误
  const statusCodeError = await errorStatusCodeCatcher(
    options,
    response.statusCode,
  );
  if (statusCodeError) return statusCodeError;
  const code = result.code;
  // 授权问题去重新登录后直接返回一个新请求
  if ([401, 403].includes(response.statusCode) || code === 400001) {
    if (options.noRedirectLogin) {
      return {
        __error: '请求错误',
        __errMsg: 'token失效',
        __requestOptions: options,
      };
    } else {
      const userStore = $store.user();
      userStore.clearUserInfo();
      await login();
      options.headers = undefined;
      const headers = await getHeaders(options);
      if (!headers) {
        return {
          __error: '请求错误',
          __errMsg: '获取token失败',
          __requestOptions: options,
        };
      }
      const retryResult = await requestInstance.request({
        ...options,
        headers,
        requestOptions: { ...options, headers },
        authed: true, // 避免登录循环
      });
      return retryResult;
    }
  }
  // 捕获接口错误
  const apiCodeError = await errorApiCodeCatcher(
    options,
    result as RegularResponseResult<AnyResult>,
    response,
  );
  if (apiCodeError) return apiCodeError;

  const data = response.data as AnyResult;
  result = {
    ...data,
    __requestOptions: options,
    __error: undefined,
  } as Result<AnyResult>;
  return result;
};

/** 请求实例 */
const requestInstance = new Request(
  {
    transformRequestData,
    requestInterceptor,
    requestInterceptorCatcher,
    responseInterceptor,
    responseInterceptorCatcher,
  },
  { baseUrl, timeout: 30 * 1000 },
);
export default requestInstance;
